import React, { Component } from 'react';
import PropTypes from 'prop-types';

import './BodyDataInput.scss';
import { BodyItemConstant as BiC } from '../../constants/InputItemConstants';
import { SingleValidation, MultipleValidation, MultipleValidationCategory as MvC } from '../../validations/BodyDataInputValidation';
import { ErrorInfo, validateSingle, validateMultiple, validateAll } from '../../validations/';
import { BodyDataInputContextConstant } from '../../constants/ComponentContextConstants';
import { SwipeableViews } from '../common-ui-parts';
import * as Q from './questions';

/**
 * 質問入力画面
 */
class BodyDataInput extends Component {
  constructor(props) {
    super(props);
    const { inputData, pageIndex } = props;
    this.state = {
      inputData,                          // すべての入力項目
      pageIndex,                          // 質問ページINDEX（0～）
      errorInfo: ErrorInfo.noneError(),   // エラー情報（メッセージ）
    };
  }

  /**
   * 項目入力時に入力項目のStateを更新するためのコールバックメソッドです。
   * State更新前に入力チェックを行い、エラー情報も併せて更新します。
   * @param {string|Object} name 入力項目名
   * @param {string|number} value 入力値
   * @param {boolean=} doValidation 入力チェックを行う場合、true（デフォルト：false）
   * @param {boolean=} validateRequried 必須入力チェックを行う場合、true（デフォルト：false）
   * @param {function=} callback State更新後に呼ばれるコールバック関数（デフォルト：何もしない）
   */
  updateState(name, value, doValidation = false, validateRequried = false, callback = () => {}) {
    let newState = { inputData: { ...this.state.inputData, [name]: value } };
    let check = ErrorInfo.noneError();

    // フォーカスアウト時のチェック
    if (doValidation) {
      check = validateSingle(name, value, SingleValidation, validateRequried);
      newState.errorInfo = (check.isError) ? this.state.errorInfo.merge(check) : this.state.errorInfo.clear([name]);
    }

    this.setState(newState, () => {
      if (check.ok) {
        callback(name, value);
      }
    });

    return check.ok;
  }

  /**
   * 自ページ全体の入力項目をチェックします。
   * 質問ページ子コンポーネント（QuestionNN#validatePage()）からコールされます。
   * @param {!Array.<string>|!Array.<Object>} names 単項目チェック対象項目名配列
   * @param {Array.<string>=} categories 相関チェック対象種別配列
   * @return {boolean} true:チェックOK, false:チェックNG（なお、自ページのエラーが含まれなければ、true）
   */
  validateQuestionPage(names, categories = []) {
    if (!Array.isArray(names) || names.length === 0) {
      throw new Error(`<質問入力画面> 引数エラー: names=${names}`);
    }

    const { inputData, errorInfo } = this.state;
    let result = errorInfo.copy();
    let isError = false;  // 自ページのエラー（true:エラーあり, false:エラーなし）

    // 自ページ全体をチェックし直すため、あらかじめ対象項目のエラー情報をクリアしておく
    result = result.clear(names.concat(categories));

    // 単項目チェック
    names.map(name => (
      validateSingle(name, inputData[name], SingleValidation, true)
    )).forEach(check => {
      if (check.isError) {
        result = result.concat(check);
        isError = true;
      }
    });

    // 自ページの単項目チェックでエラーの場合、および、チェック対象の指定がない場合は、相関チェックは行わない
    if (!isError && categories.length > 0) {
      // 相関チェック
      const check = validateMultiple(inputData, MultipleValidation, categories);
      if (check.isError) {
        result = result.concat(check);
        isError = true;
      }
    }

    // チェック結果を更新
    this.setState({ errorInfo: result });

    // 自ページのエラーが含まれなければ、チェックOK
    return !isError;
  }

  /**
   * ［入力完了］ボタンの処理
   */
  handleSave() {
    const { inputData, pageIndex } = this.state;
    // 全入力項目の単項目チェック＆相関チェック
    const result = validateAll(inputData, SingleValidation, MultipleValidation);
    this.setState({ errorInfo: result }, () => {
      if (result.ok) {
        this.props.onAction(inputData, pageIndex);
      }
    });
  }

  /**
   * ［次へ］ボタンの処理
   * @param {number=} skip ページスキップ数（省略した場合、スキップなし＝0）
   */
  handleNext(skip = 0) {
    const { inputData, pageIndex } = this.state;
    // 入力チェック（ページ単体）
    if (this._handlePageValidate(pageIndex)) {
      this._pageMove(pageIndex + skip + 1, () => {
        this.props.pageNext(inputData, this.state.pageIndex);
      });
    } else {
      this.refs[`q${pageIndex}`].scrollPageTop(); // 縦に長い画面でもエラーメッセージを表示させるため
    }
  }

  /**
   * ［戻る］ボタンの処理
   * @param {number=} skip ページスキップ数（省略した場合、スキップなし＝0）
   */
  handleBack(skip = 0) {
    const { inputData, pageIndex } = this.state;
    this._pageMove(pageIndex - skip - 1, () => {
      this.props.pageBack(inputData, this.state.pageIndex);
    });
  }

  /**
   * 飲酒習慣入力時の処理（飲酒頻度、飲酒量をクリアします）
   * @param {string|Object} name 入力項目名
   * @param {number} value 入力値
   */
  handleSelectAlcoholHabit(name, value) {
    if (name !== BiC.ALCOHOL_HABIT) return;

    // 飲酒習慣選択時の飲酒頻度、飲酒量のクリア処理
    const { inputData, errorInfo } = this.state;
    switch (value) {
      case BiC.ALCOHOL_HABIT.IS_DRINKING.value: // 飲む
      case BiC.ALCOHOL_HABIT.STOPPED.value:     // やめた
        // 何もしない
        break;
      case BiC.ALCOHOL_HABIT.NO_DRINKING.value: // 飲まない
        // 入力内容をクリア
        this.setState({
          inputData: {
            ...inputData,
            [name]: value,
            [BiC.ALCOHOL_FREQUENCY]: undefined,       // 飲酒頻度
            [BiC.ALCOHOL_AMOUNT_SAKE]: undefined,     // 飲酒量（日本酒）
            [BiC.ALCOHOL_AMOUNT_SHOCHU]: undefined,   // 飲酒量（焼酎または泡盛）
            [BiC.ALCOHOL_AMOUNT_BEER]: undefined,     // 飲酒量（ビールまたは発泡酒）
            [BiC.ALCOHOL_AMOUNT_WHISKEY]: undefined,  // 飲酒量（ウィスキーまたはブランデー）
            [BiC.ALCOHOL_AMOUNT_WINE]: undefined,     // 飲酒量（ワイン）
          },
          errorInfo: errorInfo.clear([
            BiC.ALCOHOL_FREQUENCY,
            BiC.ALCOHOL_AMOUNT_SAKE,
            BiC.ALCOHOL_AMOUNT_SHOCHU,
            BiC.ALCOHOL_AMOUNT_BEER,
            BiC.ALCOHOL_AMOUNT_WHISKEY,
            BiC.ALCOHOL_AMOUNT_WINE,
            MvC.ALCOHOL_FREQUENCY,
            MvC.ALCOHOL_AMOUNT_BEER,
            MvC.ALCOHOL_AMOUNT_WINE,
            MvC.ALCOHOL_AMOUNT_SAKE,
            MvC.ALCOHOL_AMOUNT_SHOCHU,
            MvC.ALCOHOL_AMOUNT_WHISKEY,
            MvC.ALCOHOL_AMOUNT_ALL,
          ]),
        });
        break;
      default:
        throw new Error(`<Q3.飲酒習慣> 想定外のデータ: 飲酒習慣=${value}`);
    }
  }

  /**
   * 飲酒頻度入力時の処理（飲酒量をクリアします）
   * @param {string|Object} name 入力項目名
   * @param {number} value 入力値
   */
  handleSelectAlcoholFrequency(name, value) {
    if (name !== BiC.ALCOHOL_FREQUENCY) return;

    // 飲酒頻度選択時の飲酒量のクリア処理
    const { inputData, errorInfo } = this.state;
    switch (value) {
      case undefined:                                     // 未選択（選択してください）
      case BiC.ALCOHOL_FREQUENCY.MONTH_LESS3.value:       // 月1～3日
      case BiC.ALCOHOL_FREQUENCY.WEEK_LESS2.value:        // 週1～2日
      case BiC.ALCOHOL_FREQUENCY.WEEK_LESS4.value:        // 週3～4日
      case BiC.ALCOHOL_FREQUENCY.ALMOST_EVERY_DAY.value:  // 週5～6日
      case BiC.ALCOHOL_FREQUENCY.EVERY_DAY.value:         // 毎日
        // 何もしない
        break;
      case BiC.ALCOHOL_FREQUENCY.MONTH_LESS1.value:       // 月1日未満
        // 入力内容をクリア
        this.setState({
          inputData: {
            ...inputData,
            [name]: value,
            [BiC.ALCOHOL_AMOUNT_SAKE]: undefined,     // 飲酒量（日本酒）
            [BiC.ALCOHOL_AMOUNT_SHOCHU]: undefined,   // 飲酒量（焼酎または泡盛）
            [BiC.ALCOHOL_AMOUNT_BEER]: undefined,     // 飲酒量（ビールまたは発泡酒）
            [BiC.ALCOHOL_AMOUNT_WHISKEY]: undefined,  // 飲酒量（ウィスキーまたはブランデー）
            [BiC.ALCOHOL_AMOUNT_WINE]: undefined,     // 飲酒量（ワイン）
          },
          errorInfo: errorInfo.clear([
            BiC.ALCOHOL_AMOUNT_SAKE,
            BiC.ALCOHOL_AMOUNT_SHOCHU,
            BiC.ALCOHOL_AMOUNT_BEER,
            BiC.ALCOHOL_AMOUNT_WHISKEY,
            BiC.ALCOHOL_AMOUNT_WINE,
            MvC.ALCOHOL_AMOUNT_BEER,
            MvC.ALCOHOL_AMOUNT_WINE,
            MvC.ALCOHOL_AMOUNT_SAKE,
            MvC.ALCOHOL_AMOUNT_SHOCHU,
            MvC.ALCOHOL_AMOUNT_WHISKEY,
            MvC.ALCOHOL_AMOUNT_ALL,
          ]),
        });
        break;
      default:
        throw new Error(`<Q4.飲酒頻度> 想定外のデータ: 飲酒頻度=${value}`);
    }
  }

  /**
   * 指定ページに遷移します。
   * @private
   * @param {number} index 遷移先質問ページINDEX
   * @param {function=} callback
   */
  _pageMove(index, callback = () => {}) {
    this.setState({ pageIndex: index }, () => {
      // ページ移動時に遷移先ページの内部スクロール位置をトップに変更する
      this.refs[`q${index}`].scrollPageTop();
      callback();
    });
  }

  /**
   * 指定ページの入力チェックを行います。
   * @private
   * @param {number} index チェック先質問ページINDEX
   * @return {boolean} true:チェックエラーなし, false:チェックエラーあり
   */
  _handlePageValidate(index) {
    return this.refs[`q${index}`].validatePage();
  }

  render() {
    const { pageIndex } = this.state;
    return (
      <div className="BodyDataInput">

        <div className="inputArea">
          <SwipeableViews
            index={pageIndex}
            onChengeIndex={this._pageMove.bind(this)}
            onPageValidate={this._handlePageValidate.bind(this)}
          >
          {[
            <Q.Question01 />,     // Q1. 基本情報
            <Q.Question02 />,     // Q2. 喫煙習慣
            <Q.Question03 />,     // Q3. 飲酒習慣
            <Q.Question04 />,     // Q4. 飲酒頻度
            <Q.Question05 />,     // Q5. 飲酒量
            <Q.Question06_01 />,  // Q6. 運動習慣（座っている時間）
            <Q.Question06_02 />,  // Q6. 運動習慣（立ったり歩いたりしている時間）
            <Q.Question06_03 />,  // Q6. 運動習慣（肉体労働やスポーツをする時間）
            <Q.Question07 />,     // Q7. 塩蔵品
            <Q.Question08 />,     // Q8. 糖尿病
            <Q.Question09 />,     // Q9. 血糖値
            <Q.Question10 />,     // Q10.降圧剤
            <Q.Question11 />,     // Q11.血圧
            <Q.Question12 />,     // Q12.コレステロール（HDL,LDL,中性脂肪）
            <Q.Question13 />,     // Q13.家族の既往歴
            <Q.Question14 />,     // Q14.抗体検査
            <Q.Question15 />,     // Q15.慢性胃炎
          ].map((question, index) => (
            <div key={index} className="page" >
              {React.cloneElement(question, { index, ref: `q${index}` })}
            </div>
          ))}
          </SwipeableViews>
        </div>

      </div>
    );
  }

  getChildContext() {
    return Object.freeze({
      parent: Object.freeze({
        inputData: Object.freeze(this.state.inputData),
        errorInfo: Object.freeze(this.state.errorInfo),
        pageIndex: this.state.pageIndex,
        updateState: this.updateState.bind(this),
        handleSave: this.handleSave.bind(this),
        handleNext: this.handleNext.bind(this),
        handleBack: this.handleBack.bind(this),
        handleSelectAlcoholHabit: this.handleSelectAlcoholHabit.bind(this),
        handleSelectAlcoholFrequency: this.handleSelectAlcoholFrequency.bind(this),
        validateQuestionPage: this.validateQuestionPage.bind(this),
      }),
    });
  }

}

BodyDataInput.childContextTypes = BodyDataInputContextConstant;
/*
BodyDataInput.propTypes = {
  inputData: PropTypes.shape({
    [BiC.GENDER]: PropTypes.number,
    [BiC.AGE]: PropTypes.number,
    [BiC.BODY_HEIGHT]: PropTypes.number,
    [BiC.BODY_WEIGHT]: PropTypes.number,
    [BiC.SMOKING_HABIT]: PropTypes.number,
    [BiC.ALCOHOL_HABIT]: PropTypes.number,
    [BiC.ALCOHOL_FREQUENCY]: PropTypes.number,
    [BiC.ALCOHOL_AMOUNT_SAKE]: PropTypes.number,
    [BiC.ALCOHOL_AMOUNT_SHOCHU]: PropTypes.number,
    [BiC.ALCOHOL_AMOUNT_BEER]: PropTypes.number,
    [BiC.ALCOHOL_AMOUNT_WHISKEY]: PropTypes.number,
    [BiC.ALCOHOL_AMOUNT_WINE]: PropTypes.number,
    [BiC.EXERCISE_TIME_SITTING]: PropTypes.number,
    [BiC.EXERCISE_TIME_STANDING]: PropTypes.number,
    [BiC.EXERCISE_TIME_LABORING]: PropTypes.number,
    [BiC.SALINE_FOOD]: PropTypes.number,
    [BiC.DIABETES_CURE_FLG]: PropTypes.number,
    [BiC.BLOOD_SUGAR_LEVEL_FASTING]: PropTypes.number,
    [BiC.BLOOD_SUGAR_LEVEL_ANYTIME]: PropTypes.number,
    [BiC.ANTIHYPERTENSIVE_FLG]: PropTypes.number,
    [BiC.MAX_BLOOD_PRESSURE]: PropTypes.number,
    [BiC.MIN_BLOOD_PRESSURE]: PropTypes.number,
    [BiC.HDL_CHOLESTEROL]: PropTypes.number,
    [BiC.LDL_CHOLESTEROL]: PropTypes.number,
    [BiC.TRIGLYCERIDE]: PropTypes.number,
    [BiC.TOTAL_CHOLESTEROL]: PropTypes.number,
    [BiC.FAMILY_ANAMNESIS_FLG]: PropTypes.number,
    [BiC.HELICOBACTER_PYLORI_FLG]: PropTypes.number,
    [BiC.CHRONIC_GASTRITIS_FLG]: PropTypes.number,
  }),
  pageIndex: PropTypes.number,
  onAction: PropTypes.func,
  onCancel: PropTypes.func,
  pageNext: PropTypes.func,
  pageBack: PropTypes.func,
};*/

BodyDataInput.defaultProps = {
  inputData: {},
  pageIndex: 0,
  onAction: () => {},
  onCancel: () => {},
  pageNext: () => {},
  pageBack: () => {},
};

export default BodyDataInput
